package p28_消息转换器;

import cn.hutool.core.collection.ListUtil;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.mock.http.MockHttpInputMessage;
import org.springframework.mock.http.MockHttpOutputMessage;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import p21_参数解析器.Person;
import p27_返回值处理器.MyController;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息转换器(R)(RequestResponseBodyMethodProcessor处理器内部持有了多个消息转换器)
 * 用于在java对象与请求体/响应体数据之间的转换
 * 为什么只能用在请求体/响应体数据呢?
 *    因为请求体/响应体数据可以是任意格式的字符串，对应不同格式的字符串，有不同类型的消息转换器来处理
 *    而请求头/响应头的数据固定是kv键值对的，已知的数据格式，不必使用消息转换器
 */
public class _MessageConverter {

    public static void main(String[] args) throws IOException, NoSuchMethodException, HttpMediaTypeNotAcceptableException {
        //obj -> json
        objectToJson();
        //obj -> xml
        objectToXml();
        //json -> obj
        jsonToObj();
        //xml -> obj
        xmlToObj();

        /**
         * 下面模拟返回值处理器，利用消息转换器，将转换后的字符串存入响应体
         */
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        //request.addHeader("Accept", "application/json");
        //response.setContentType("application/xml");

        /**
         * RequestResponseBodyMethodProcessor是作为处理带有@ResponseBody的控制器方法的返回值
         * RequestResponseBodyMethodProcessor可以拥有多个消息转换器，具体使用哪个呢?
         *      1.默认情况下，谁先定义就使用哪个
         *      2.请求头中带有Accept属性，就会优先采用对应消息转换器
         *      3.响应头带有content-type，就会优先采用对象消息转换器
         *  优先级从上到下依次递增
         *  @RequestMapping(produces = "application/json"),这个设置就是设置响应头的content-type
         */
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(
                ListUtil.of(new MappingJackson2XmlHttpMessageConverter(), new MappingJackson2HttpMessageConverter())
        );
        processor.handleReturnValue(
                new Person("张三", 33),   //要解析的对象
                new MethodParameter(MyController.class.getMethod("test7"), -1), //控制器方法的返回值
                new ModelAndViewContainer(),
                webRequest
        );
        System.out.println(new String(response.getContentAsByteArray()));
    }

    /**
     * 将java对象转为json格式的字符串
     * @throws IOException
     */
    public static void objectToJson() throws IOException {
        MockHttpOutputMessage message = new MockHttpOutputMessage();
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //判断该消息处理器能否转换
        if (converter.canWrite(Person.class, MediaType.APPLICATION_JSON)) {
            //将转化后的字符串写进message
            converter.write(new Person("王五", 44), MediaType.APPLICATION_JSON, message);
            System.out.println(new String(message.getBodyAsBytes()));
        }
    }

    /**
     * 将java对象转为xml格式的字符串
     * @throws IOException
     */
    public static void objectToXml() throws IOException {
        MockHttpOutputMessage message = new MockHttpOutputMessage();
        MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();
        //判断该消息处理器能否转换
        if (converter.canWrite(Person.class, MediaType.APPLICATION_XML)) {
            //将转化后的字符串写进message
            converter.write(new Person("王五", 44), MediaType.APPLICATION_XML, message);
            System.out.println(new String(message.getBodyAsBytes()));
        }
    }

    /**
     * 将json字符串转为java对象
     */
    public static void jsonToObj() throws IOException {
        MockHttpInputMessage message = new MockHttpInputMessage("{\"name\":\"陈留\",\"age\":22}".getBytes(StandardCharsets.UTF_8));
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        if (converter.canRead(Person.class, MediaType.APPLICATION_JSON)) {
            Object p = converter.read(Person.class, message);
            System.out.println(p);
        }
    }

    /**
     * 将xml字符串转为java对象
     */
    public static void xmlToObj() throws IOException {
        MockHttpInputMessage message = new MockHttpInputMessage("<Person><name>朝气</name><age>23</age></Person>".getBytes(StandardCharsets.UTF_8));
        MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();
        if (converter.canRead(Person.class, MediaType.APPLICATION_XML)) {
            Object p = converter.read(Person.class, message);
            System.out.println(p);
        }
    }
}
